package com.shujia.wyh.controller;

import com.shujia.wyh.entity.MyTypedTuple;
import com.shujia.wyh.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import redis.clients.jedis.*;

import java.time.Duration;
import java.util.*;

/**
 * StringRedisTemplate: key是String类型，value也是String类型
 * RedisTemplate：Key和Object类型都是Object类型，将key和value当作对象来看待
 * 既然将key和value当作对象来看待,那么存储的时候，就是存一个对象
 * 对象存储到数据库中，是一个持久化的过程，对象持久化到数据库中，需要对对象进行序列化
 * <p>
 * <p>
 * 回忆一下java中的序列化，对象的类必须要实现serializable接口，其次生成一个序列化ID
 * 在java中String类实现了serializable接口，可能在使用RedisTemplate对象的时候能够偶读取到对应的数据
 * 一定是可以的嘛？
 * <p>
 * <p>
 * 由于StringRedisTemplate是RedisTemplate子类，大部分的方法，两个都差不多。
 */

@Controller
@ResponseBody
@RequestMapping("redistest")
public class RedisApiController {

    //springboot操作redis可以使用自己提供的redis依赖，父工程中已经写好了两个类供我们使用
    //RedisTemplate和StringRedisTemplate
    //通过对象注入的方式，使用该类的对象
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;


    @RequestMapping("test")
    public String hello() {
        return "测试模板springboot";
    }

    /**
     * 对redis中数据类型做操作
     */
    //使用springboot的方式操作redis集群
    @RequestMapping("rediscluster2")
    public String testRedisCluster2(){
        Set<String> keys = stringRedisTemplate.keys("*");
        String s = keys.toString();
        return s;
    }


    //纯java的方式连接redis集群
    @RequestMapping("rediscluster1")
    public String testRedisCluster1() {

        //定义一个Set集合，存集群中每个节点的ip地址和端口号
        HashSet<HostAndPort> set = new HashSet<>();

        //创建HostAndPort对象，封装ip地址和端口号
        //集群中总共有7个节点
        HostAndPort h1 = new HostAndPort("192.168.254.150", 7000);
        HostAndPort h2 = new HostAndPort("192.168.254.150", 7001);
        HostAndPort h3 = new HostAndPort("192.168.254.150", 7002);
        HostAndPort h4 = new HostAndPort("192.168.254.150", 7003);
        HostAndPort h5 = new HostAndPort("192.168.254.150", 7004);
        HostAndPort h6 = new HostAndPort("192.168.254.150", 7005);
        HostAndPort h7 = new HostAndPort("192.168.254.150", 7006);

        //把节点添加到集合中
        set.add(h1);
        set.add(h2);
        set.add(h3);
        set.add(h4);
        set.add(h5);
        set.add(h6);
        set.add(h7);


        JedisCluster jedisCluster = new JedisCluster(set);

//        Set<String> keys = jedisCluster.keys("*");
//        System.out.println(keys);

        String name = jedisCluster.get("name");
        String age = jedisCluster.get("age");

        return name + ":" + age;
    }

    //使用RedisTemplate对象
    @RequestMapping("test2")
    public String test2() {
//        //构建redis资源连接池配置
//        JedisPoolConfig poolConfig = new JedisPoolConfig();
//        //给定个数
//        poolConfig.setMaxTotal(100);
//
//        //根据这个配置来创建redis连接池
//        JedisPool jedisPool = new JedisPool(poolConfig, "192.168.40.110", 7000);
//
//        //在连接池中构建redis连接
//        Jedis jedis = jedisPool.getResource();
//
//        //拿着这个连接操作redis
//        String name1 = jedis.get("name");
//        System.out.println(name1);
//
//        //操作完后将连接归还给连接池
//        jedis.close();


        /**
         *  分析为什么是null?
         *  首先，name我们使用StringRedisTemplate,而我们现在用RedisTemplate去读，两个类都不一样，可能读不到
         *  我们回想一下刚刚提到的一点，RedisTemplate会将key和value当作对象来看待，所以需要对key和value做序列化
         *  那么RedisTemplate就应该把我们传进去的name做序列化，”name“ --->"namexxxx",然后拿着序列化后的值去redis中找，而redis中并没有namexxxx，所以结果为null
         *
         *  String类刚刚才看的，已经实现了serializable接口，为什么不行呢？
         *  所以这里，用的应该不是 jdk自己序列化，而是redis中特有的序列化方式
         *
         *  我们使用了redis特有的序列化方式后，结果却依旧没有出来。
         *  序列化ID,再次插入一次，让两次序列化ID保持一直就可以了。
         *
         */
        redisTemplate.setKeySerializer(new StringRedisSerializer());
//        redisTemplate.opsForValue().set("name","xiaohu");
//        Object name = redisTemplate.opsForValue().get("name");
//        System.out.println(name); //null
        //需求：创建学生对象，插入到redis中
//        Student s1 = new Student("李凯迪", 18);
//        redisTemplate.opsForValue().set("1001",s1);
        Object o = redisTemplate.opsForValue().get("1001");
        System.out.println(o);

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.opsForHash().put("1002", "name", "lkd");


        return "测试使用RedisTemplate对象成功！";
    }


    //对Hash类型做操作
    @RequestMapping("testHash")
    public String testHash() {
        //将值用一个map集合进行封装
        HashMap<String, String> map = new HashMap<>();
        map.put("name", "小虎");
        map.put("age", "18");
        map.put("like", "睡觉");
        map.put("gender", "男");
        stringRedisTemplate.opsForHash().putAll("hash1", map);

        ArrayList<Object> arrayList = new ArrayList<>();
        arrayList.add("name");
        arrayList.add("age");
        arrayList.add("like");
        arrayList.add("gender");
        List<Object> hash1 = stringRedisTemplate.opsForHash().multiGet("hash1", arrayList);
        for (Object o : hash1) {
            System.out.println(o);
        }

        return "测试Hash类型成功！！";
    }


    /**
     * 添加多个数据值
     * public Long add(K key, Set<TypedTuple<V>> tuples) {
     * byte[] rawKey = rawKey(key);
     * Set<Tuple> rawValues = rawTupleValues(tuples);
     * return execute(connection -> connection.zAdd(rawKey, rawValues));
     * }
     */
    @RequestMapping("testZSet")
    public String testZSet() {
        HashSet<ZSetOperations.TypedTuple<String>> myTypedTuples = new HashSet<>();
        //封装自己的值和分数
        myTypedTuples.add(new MyTypedTuple("小虎", 100.0));
        myTypedTuples.add(new MyTypedTuple("李凯迪", 200.0));
        myTypedTuples.add(new MyTypedTuple("侯凯迪", 300.0));
        myTypedTuples.add(new MyTypedTuple("音旭辉", 150.0));
        stringRedisTemplate.opsForZSet().add("zset1", myTypedTuples);

        //获取ZSet类型的数据
        Set<String> zset = stringRedisTemplate.opsForZSet().range("zset1", 0, -1);
        for (String s : zset) {
            System.out.println(s);
        }

        System.out.println("==============================================");
        //获取元素的同时获取分数 [min,max]
        Set<ZSetOperations.TypedTuple<String>> zset1 = stringRedisTemplate.opsForZSet().rangeByScoreWithScores("zset1", 200, 300);
        for (ZSetOperations.TypedTuple<String> stringTypedTuple : zset1) {
            String value = stringTypedTuple.getValue();
            Double score = stringTypedTuple.getScore();
            System.out.println(value + ":" + score);
        }
        System.out.println("==============================================");
        //倒序遍历
        Set<String> zset11 = stringRedisTemplate.opsForZSet().reverseRangeByScore("zset1", 0, 1000);
        if (zset11 != null) {
            for (String s : zset11) {
                System.out.println(s);
            }
        }
        System.out.println("==============================================");
        //倒序遍历并且输出分数
        Set<ZSetOperations.TypedTuple<String>> zset12 = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores("zset1", 0, 1000);
        for (ZSetOperations.TypedTuple<String> stringTypedTuple : zset12) {
            String value = stringTypedTuple.getValue();
            Double score = stringTypedTuple.getScore();
            System.out.println(value + ":" + score);
        }


        return "测试ZSet类型成功";
    }


    //对Set类型做操作
    @RequestMapping("testSet")
    public String testSet() {
        //插入多个数据
        stringRedisTemplate.opsForSet().add("set1", "hadoop", "hive", "ETL", "Hbase", "阿里云");
        //获取值
        Set<String> set1 = stringRedisTemplate.opsForSet().members("set1");
        for (String s : set1) {
            System.out.println(s);
        }
        return "操作Set类型成功！！";
    }


    //对list类型做操作
    @RequestMapping("testList")
    public String testList() {
//        stringRedisTemplate.opsForList().leftPush("list1","java");
//        stringRedisTemplate.opsForList().leftPush("list1","linux");
//        stringRedisTemplate.opsForList().leftPush("list1","mysql");
//        stringRedisTemplate.opsForList().leftPush("list1","maven-git");
//        stringRedisTemplate.opsForList().leftPush("list1","springboot");
//        stringRedisTemplate.opsForList().leftPush("list1","redis");
//        stringRedisTemplate.opsForList().leftPush("list1","hadoop");
        //一次性插入多个值
//        ArrayList<String> arrayList = new ArrayList<>();
//        arrayList.add("java");
//        arrayList.add("linux");
//        arrayList.add("mysql");
//        arrayList.add("maven-git");
//        arrayList.add("springboot");
//        arrayList.add("redis");
//        arrayList.add("hadoop");
//        stringRedisTemplate.opsForList().leftPushAll("list1",arrayList);
        //遍历
        List<String> list = stringRedisTemplate.opsForList().range("list1", 0, -1);
        //断言
//        assert list != null;
        if (list != null) {
            for (String s : list) {
                System.out.println(s);
            }
        }

        //在redis值左边插入数据
//        stringRedisTemplate.opsForList().leftPush("list1","redis","test1");
        //在redis值右边插入数据
//        stringRedisTemplate.opsForList().rightPush("list1","redis","test2");
        //保留一定的元素
        stringRedisTemplate.opsForList().trim("list1", 0, 5);
        return "测试list类型";
    }


    //对字符类型做操作
    @RequestMapping("testString")
    public String testString() {
        //链式调用
        //opsForValue操作值是String类型
        //set方法是没有返回值的
        stringRedisTemplate.opsForValue().set("name", "xiaohu");
        stringRedisTemplate.opsForValue().set("age", "18");
        stringRedisTemplate.opsForValue().set("like", "睡觉");
        stringRedisTemplate.opsForValue().set("gender", "男");
        //设置值的同时设置过期时间
        stringRedisTemplate.opsForValue().set("test", "test", Duration.ofSeconds(20L));
        //获取值，通过key获取
        String name = stringRedisTemplate.opsForValue().get("name");
        System.out.println(name);
        //一次获取多个key对应的value值
        //将你要获取所有的key组成一个集合
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("name");
        arrayList.add("age");
        arrayList.add("like");
        arrayList.add("gender");
        List<String> strings = stringRedisTemplate.opsForValue().multiGet(arrayList);
        for (String string : strings) {
            System.out.println(string);
        }
        //一次型插入多个键值对
        HashMap<String, String> map = new HashMap<>();
        map.put("address", "安徽合肥");
        map.put("phone", "苹果20");
        stringRedisTemplate.opsForValue().multiSet(map);
        //追加
//        stringRedisTemplate.opsForValue().append()

        return "测试字符串类型";
    }


    @RequestMapping("testKey")
    public String testKey() {
        //获取当前库中的所有的键
        Set<String> keys = stringRedisTemplate.keys("*");
        if (keys != null) {
            for (String key : keys) {
                System.out.println(key);
            }
        } else {
            System.out.println("该库中没有键存在");
        }
        //给键设置过期时间
        //设置分钟级别
        Boolean k1 = stringRedisTemplate.expire("k1", Duration.ofMinutes(1L));
        System.out.println(k1);
        //设置秒级别
        Boolean k2 = stringRedisTemplate.expire("k2", Duration.ofSeconds(20L));
        System.out.println(k2);
        //判断key是否存在  hasKey
        Boolean k11 = stringRedisTemplate.hasKey("k1");
        System.out.println(k11);
        Boolean k12 = stringRedisTemplate.hasKey("name");
        System.out.println(k12);
        //将键移动到其他库中
        stringRedisTemplate.move("name", 1);
        //将键删除
        //删除一个键
        stringRedisTemplate.delete("hash1");
        //删除多个键
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("k1xork2");
        arrayList.add("list1");
        arrayList.add("list2");
        stringRedisTemplate.delete(arrayList);
        //查看键对应的值的类型
        //自学的方法：
        //1、查看方法的名字
        //2、查看方法需要传的参数类型与个数
        //3、查看返回值类型
        //4、查看源码的实现（需要你掌握java的面向对象开发的基础）
        DataType set1 = stringRedisTemplate.type("zset1");
        System.out.println(set1.code());


        return "测试与redis键相关的操作成功！！";
    }

}
